/// Copyright (c) 2021 Razeware LLC
/// 
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to deal
/// in the Software without restriction, including without limitation the rights
/// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
/// copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
/// 
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
/// 
/// Notwithstanding the foregoing, you may not use, copy, modify, merge, publish,
/// distribute, sublicense, create a derivative work, and/or sell copies of the
/// Software in any work that is designed, intended, or marketed for pedagogical or
/// instructional purposes related to programming, coding, application development,
/// or information technology.  Permission for such use, copying, modification,
/// merger, publication, distribution, sublicensing, creation of derivative works,
/// or sale is expressly withheld.
/// 
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
/// THE SOFTWARE.

import XCTest
@testable import DogPatch

class ImageClientTests: XCTestCase {
  var mockSession: MockURLSession!
  var sut: ImageClient!
  var url: URL!
  
  var receivedDataTask: MockURLSessionDataTask!
  var receivedError: Error!
  var receivedImage: UIImage!
  
  
  var service: ImageService{
    return sut as ImageService
  }
  
  override func setUp() {
    super.setUp()
    mockSession = MockURLSession()
    url = URL.init(string: "http://www.example.com/v1/logo.png")!
    sut = ImageClient.init(responseQueue: nil, session: mockSession)
  }
  
  override func tearDown() {
    mockSession = nil
    sut = nil
    
    receivedDataTask = nil
    receivedError = nil
    receivedImage = nil
    super.tearDown()
  }
  
  func test_shared_responseQueue(){
    XCTAssertEqual(ImageClient.shared.responseQueue, .main)
  }
  
  func test_shared_session(){
    XCTAssertEqual(ImageClient.shared.session, .shared)
  }
  
  func test_init_responseQueue(){
    XCTAssertEqual(sut.responseQueue, nil)
  }
  
  func test_init_session(){
    XCTAssertEqual(sut.session, mockSession)
  }
  
  func test_init_cachedImagesForURL(){
    XCTAssertEqual(sut.cachedImagesForURL, [:])
  }
  
  func test_init_cachedTasksForImageView(){
    XCTAssertEqual(sut.cachedTasksForImageView, [:])
  }
  
  //MARK: test ImageService
  func test_conformsTo_imageService(){
    XCTAssertTrue((sut as AnyObject) is ImageService)
  }
  
  func test_declares_downloadImage(){
//    service.down
    let _ = service.downloadImage(fromURL: url) { (_, _) in
      
    }
  }
  
  func test_downloadImage_createExpectedTask(){
    whenDownloadImage()
    XCTAssertEqual(receivedDataTask.url, url)
  }
  
  func test_downloadImage_taskResumeCalled(){
    whenDownloadImage()
    XCTAssertTrue(receivedDataTask.calledResume)
  }
  
  func whenDownloadImage(image: UIImage? = nil, error: Error? = nil){
    receivedDataTask = service.downloadImage(fromURL: url) { [weak self] image, error in
      guard let self = self else{
        return
      }
      self.receivedImage = image
      self.receivedError = error
    } as? MockURLSessionDataTask
    
    guard let receivedDataTask = receivedDataTask else{
      return
    }
    
    if let image = image{
      receivedDataTask.completionHandler(image.pngData(), nil, nil)
    }else if let error = error{
      receivedDataTask.completionHandler(nil, nil, error)
    }
  }
  
  func test_downloadImage_givenImage_callsCompletionWithImage() { // given
    let expectedImage = UIImage(named: "happy_dog")!
    whenDownloadImage(image: expectedImage, error: nil)
    XCTAssertEqual(expectedImage.pngData(), receivedImage.pngData())
    
  }
  
  func test_downloadImage_givenError_callsCompletionWithError() {
    let expectedError = NSError(domain: "com.example", code: 42, userInfo: nil)
    whenDownloadImage(image: nil, error: expectedError)
    XCTAssertEqual(receivedError as NSError, expectedError)
  }
  
  func test_downloadImage_givenError_dispatchesToResponseQueue() {
    mockSession.givenDispatchQueue()
    let expectedError = NSError(domain: "com.example", code: 42, userInfo: nil)
    sut = ImageClient(responseQueue: .main, session: mockSession)
    var receivedThread: Thread!
    let exp = expectation(description: "test_downloadImage_givenError_dispatchesToResponseQueue")
    let task = sut.downloadImage(fromURL: url) { (image, error) in
      receivedThread = Thread.current
      exp.fulfill()
    } as! MockURLSessionDataTask
    task.completionHandler(nil, nil, expectedError)
    wait(for: [exp], timeout: 200)
    XCTAssertTrue(receivedThread.isMainThread)
  }
}
